﻿namespace MicroCloud.Entity
{
    /// <summary>
    /// 设计时数据库上下文工厂基类(用于执行迁移)
    /// </summary>
    /// <typeparam name="TDbContext">数据上下文类型</typeparam>
    public abstract class DesignTimeDbContextFactoryBase<TDbContext> : IDesignTimeDbContextFactory<TDbContext>
        where TDbContext : DbContext, IDbContext
    {
        /// <summary>
        /// 初始化一个 <see cref="DesignTimeDbContextFactoryBase{TDbContext}"/> 类型的新实例
        /// </summary>
        /// <param name="provider">服务提供者</param>
        protected DesignTimeDbContextFactoryBase(IServiceProvider provider)
        {
            ServiceProvider = provider;
        }

        /// <summary>
        /// 获取 服务提供者
        /// </summary>
        protected IServiceProvider ServiceProvider { get; private set; }

        #region IDesignTimeDbContextFactory
        /// <summary>
        /// 创建一个数据上下文实例
        /// </summary>
        /// <param name="args">参数</param>
        /// <returns></returns>
        public virtual TDbContext CreateDbContext(string[] args)
        {
            //服务集合及服务提供者
            IServiceCollection services = CreateDesignTimeServiceCollection();
            ServiceLocator.Instance.SetServiceCollection(services);

            ServiceProvider ??= CreateDesignTimeServiceProvider(services);
            ServiceLocator.Instance.SetApplicationServiceProvider(ServiceProvider);

            //打印一个EF CodeFirst的彩蛋
            ConsoleHelper.WriteLine($@"
               __  ___     ___          _        ___ _          _   
              /__\/ __\   / __\___   __| | ___  / __(_)_ __ ___| |_ 
             /_\ / _\    / /  / _ \ / _` |/ _ \/ _\ | | '__/ __| __|
            //__/ /     / /__| (_) | (_| |  __/ /   | | |  \__ \ |_ 
            \__/\/      \____/\___/ \__,_|\___\/    |_|_|  |___/\__|", ConsoleColor.DarkMagenta);

            //设置迁移的程序集
            var dbContextOptions = ServiceProvider.GetMicroCloudOptions()?.GetDbContextOptions(typeof(TDbContext));
            if (dbContextOptions?.MigrationAssemblyName?.IsMissing() == true)
            {
                string migrationAssemblyName = GetType().Assembly.GetName().Name;   //获取当前的程序集名称
                ServiceExtensions.MigrationAssemblyName = migrationAssemblyName;
            }
            else
            {
                ServiceExtensions.MigrationAssemblyName = dbContextOptions.MigrationAssemblyName;
            }
            ConsoleHelper.WriteLine($"迁移的程序集 {ServiceExtensions.MigrationAssemblyName}", ConsoleColor.DarkMagenta);

            //实体管理器
            IEntityManager entityManager = ServiceProvider.GetRequiredService<IEntityManager>();
            entityManager.Initialize();

            //数据上下文配置构建器
            DbContextOptionsBuilder builder = new DbContextOptionsBuilder<TDbContext>();
            builder = ServiceProvider.BuildDbContextOptionsBuilder<TDbContext>(builder);

            //上下文实例
            TDbContext dbContext = (TDbContext)ActivatorUtilities.CreateInstance(ServiceProvider, typeof(TDbContext), builder.Options);
            return dbContext;
        }

        #endregion

        #region 虚方法
        /// <summary>
        /// 创建设计时使用的ServiceCollection，主要用于执行 Add-Migration 功能
        /// </summary>
        /// <returns>服务集合</returns>
        protected virtual IServiceCollection CreateDesignTimeServiceCollection()
        {
            IServiceCollection services = new ServiceCollection();
            return services;
        }
        
        /// <summary>
        /// 创建设计时使用的ServiceProvider，主要用于执行 Add-Migration 功能
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <returns>服务提供者</returns>
        protected virtual IServiceProvider CreateDesignTimeServiceProvider(IServiceCollection services)
        {
            services ??= new ServiceCollection();
            #region 精准加载
            //Startup startup = new();
            //startup.ConfigureServices(services);
            #endregion

            services.AddMicroCloud().AddPacks();//自动加载所有模块，并指定排除特定模块            
            IServiceProvider provider = services.BuildServiceProvider();

            return provider;
        }

        #endregion
    }
}